/*
 [The "BSD license"]
 Copyright (c) 2011-2011 Joel Li (李家智)
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
 3. The name of the author may not be used to endorse or promote products
     derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.bee.tl.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.antlr.runtime.Token;

/**
 * 解释执行保存模板运行的变量，同时，也保存了模板的常量
 * 
 * @author joelli
 * @since 0.5
 * 
 */
public class Context
{
	Map<String, Object> vars = new HashMap<String, Object>();
	Map text = new HashMap();
	Set<String> setList = null;
	Context orginalContext = null;
	// boolean orginalContextChanged = false;
	Set<String> rawList = null;
	Context parent = null;

	public Context()
	{

	}

	/**
	 * 
	 * 构造一个新的Context，变量继承parent，对应与语法里的
	 * 
	 * <pre>
	 * {
	 * 	var name = user.name;
	 * 	// parent context
	 * 	{
	 * 		var id = name + &quot;_id&quot;;
	 * 		// new content
	 * 	}
	 * }
	 * </pre>
	 * 
	 * @param parent
	 */
	public Context(Context parent)
	{
		this();
		this.parent = parent;

	}

	public void set(String name, Object value)
	{
		if (parent != null)
			throw new RuntimeException("parent is not null，run coding");
		if (setList == null)
		{
			setList = new HashSet<String>(10);

		}
		setList.add(name);
		if (orginalContext != null)
			orginalContext.vars.put(name, value);
		this.vars.put(name, value);
	}

	/* 指示此容器包含对象为Object，不需要预编译进行类型推测 */
	public void setRawValue(String name, Object value)
	{
		this.set(name, value);
		if (rawList == null)
		{
			rawList = new HashSet<String>();
		}
		rawList.add(name);
		if (orginalContext != null)
			orginalContext.rawList.add(name);

	}

	public Set<String> getRawList()
	{

		return rawList;
	}

	/**
	 * 得到一个与模板最开始赋值的一样的Context
	 * 
	 * @return
	 */
	public Context getNewContext()
	{

		if (parent != null)
		{
			return parent.getNewContext();
		}
		else
		{
			if (orginalContext == null)
			{
				orginalContext = new Context();
				if (rawList != null)
				{
					orginalContext.rawList = new HashSet<String>();
					orginalContext.rawList.addAll(rawList);
				}

				if (setList != null)
				{
					orginalContext.setList = new HashSet<String>();
					orginalContext.setList.addAll(setList);
					for (String str : setList)
					{
						orginalContext.vars.put(str, vars.get(str));
					}
				}

				orginalContext.text = this.text;

			}

			return orginalContext;
		}

	}

	/**
	 * 模板种定义一个临时变量并赋值
	 * 
	 * @param name
	 * @param value
	 */
	protected void defineVar(String name, Object value)
	{

		if (this.contain(name))
		{
			throw new RuntimeException("变量已经定义");
		}
		vars.put(name, value);
	}

	protected void defineVar(String name, Object value, Token token)
	{

		if (this.contain(name))
		{
			throw new BeeRuntimeException(BeeRuntimeException.VAR_ALREADY_DEFINED, token, "变量已经定义");
		}
		vars.put(name, value);
	}

	/**
	 * 模板临时变量赋值
	 * 
	 * @param name
	 * @param value
	 */
	protected void setVar(String name, Object value)
	{
		if (setList != null && setList.contains(name))
		{
			throw new RuntimeException("变量只读，只有临时变量才能改");
		}
		if (this.vars.containsKey(name))
		{
			vars.put(name, value);

		}
		else if (parent != null && parent.contain(name))
		{
			parent.setVar(name, value);
		}
		else
		{
			throw new RuntimeException("变量未定义");

		}

	}

	public Object getVar(String name)
	{
		if (this.vars.containsKey(name))
		{
			return vars.get(name);

		}
		else
		{
			if (parent != null)
			{
				return parent.getVar(name);
			}
			else
			{
				throw new NullPointerException("符号" + name + "未发现");

			}

		}

	}

	/**
	 * 为NULL等内置变量的时候调用此 2011-6-28 javamonkey
	 */
	protected Object getVarWithoutException(String name)
	{
		if (this.vars.containsKey(name))
		{
			return vars.get(name);

		}
		else
		{
			if (parent != null)
			{
				return parent.getVarWithoutException(name);
			}
			else
			{
				return null;

			}

		}
	}

	/**
	 * 当前scope是否包含变量
	 * 
	 * @param name
	 * @return
	 */
	protected boolean contain(String name)
	{
		return vars.containsKey(name) || (this.parent != null && this.parent.contain(name));

	}

	protected void putTextVar(Map map)
	{
		this.text.putAll(map);
	}

	protected Object getTextVar(String key)
	{
		if (this.text.containsKey(key))
		{
			return text.get(key);

		}
		else if (parent != null)
		{
			return parent.getTextVar(key);
		}
		else
		{
			throw new NullPointerException(key);
		}

	}

	protected Object getValueAsMapListKey(Object o, Object exp)
	{
		if (o instanceof Map)
		{
			Map map = (Map) o;
			return map.get(exp);
		}
		else if (o instanceof List)
		{
			List list = (List) o;
			BeeNumber index = (BeeNumber) exp;
			int i = index.intValue();
			return list.get(i);
		}
		else if (o.getClass().isArray())
		{
			Object[] array = (Object[]) o;
			BeeNumber index = (BeeNumber) exp;
			int i = index.intValue();
			return array[i];
		}

		else
		{
			// 并不支持其他集合类型
			throw new IllegalArgumentException("Must be Map or List,Array");
		}
	}

	protected Object getValueAttribute(Object o, String attrName, BeeCommonNodeTree node)
	{

		try
		{

			Method m = (Method) node.getCached();
			if (m == null)
			{
				m = PropertyUtil.getReadMethod(o.getClass(), attrName);
				if (m == null)
				{
					throw new RuntimeException("Object '" + o + "' call attrbute " + attrName
							+ " with error:no read method " + attrName);
				}
				node.setCached(m);
			}

			Object r = m.invoke(o, new Object[0]);
			return r;

		}
		catch (IllegalArgumentException e)
		{
			// TODO Auto-generated catch block
			throw new RuntimeException("Object '" + o + "' call attrbute " + attrName + " with error:" + e.getMessage());
		}
		catch (IllegalAccessException e)
		{
			// TODO Auto-generated catch block
			throw new RuntimeException("Object '" + o + "' call attrbute " + attrName + " with error:" + e.getMessage());
		}
		catch (InvocationTargetException e)
		{
			// TODO Auto-generated catch block
			throw new RuntimeException("Object '" + o + "' call attrbute " + attrName + " with error:" + e.getMessage());
		}

	}

	public Map<String, Object> getVars()
	{
		return vars;
	}

	public void setVars(Map<String, Object> vars)
	{
		this.vars = vars;
	}

	public Map<String, Object> getText()
	{
		return text;
	}

	public void setText(Map<String, Object> text)
	{
		this.text = text;
	}

}
